/**
 * 
 */
package org.gigi.spring;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;

import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.gigi.core.web.JacksonMixIns;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;

/**
 * 扩展
 * 
 * writeInternal方法支持Jackson注释。
 * 
 * @author Eugene Wang 2011-9-6
 * @see
 */
public class GigiJacksonHttpMessageConverter extends
		MappingJacksonHttpMessageConverter {

	private boolean prefixJson = false;
	private ObjectMapper objectMapper = new ObjectMapper();

	public GigiJacksonHttpMessageConverter() {
		super();
		this.setObjectMapper(objectMapper);
	}

	@Override
	public void setObjectMapper(ObjectMapper objectMapper) {
		this.objectMapper = objectMapper;
		super.setObjectMapper(objectMapper);
	}

	@Override
	public void setPrefixJson(boolean prefixJson) {
		this.prefixJson = prefixJson;
		super.setPrefixJson(prefixJson);
	}

	@Override
	/*
	 * 相对于超类，支持Jackson注解。
	 * (non-Javadoc)
	 * @see org.springframework.http.converter.json.MappingJacksonHttpMessageConverter#writeInternal(java.lang.Object, org.springframework.http.HttpOutputMessage)
	 */
	protected void writeInternal(Object o, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
		JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
		JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
		try {
			if (this.prefixJson) {
				jsonGenerator.writeRaw("{} && ");
			}
			if (o instanceof JacksonMixIns) {
				JacksonMixIns mixInResponse = (JacksonMixIns) o;
				Map<Class<?>, Class<?>> mixins = mixInResponse.getMixInAnnotations();
				if (mixins != null && !mixins.isEmpty()) {
					SerializationConfig config = this.objectMapper.copySerializationConfig();
					config.setMixInAnnotations(mixins);
					this.objectMapper.writeValue(jsonGenerator, o, config);
					return;
				}
			}
			this.objectMapper.writeValue(jsonGenerator, o);
		}
		catch (JsonGenerationException ex) {
			throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
		}
	}

	private JsonEncoding getEncoding(MediaType contentType) {
		if (contentType != null && contentType.getCharSet() != null) {
			Charset charset = contentType.getCharSet();
			for (JsonEncoding encoding : JsonEncoding.values()) {
				if (charset.name().equals(encoding.getJavaName())) {
					return encoding;
				}
			}
		}
		return JsonEncoding.UTF8;
	}
}
